package xyz.xpman.prisonerfitness.kit;

import android.annotation.SuppressLint;
import android.os.Build;

import androidx.annotation.RequiresApi;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.chrono.ChronoLocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Objects;

/**
 * TimeKit 用于简化 JDK 8 新增的时间 API
 *
 *
 * 经测试，SimpleDateFormat 比 DateTimeFormatter 对 pattern 的支持更好
 * 对于同样的 pattern 值 "yyyy-MM-dd HH:mm:ss"，前者可以转换 "2020-06-9 12:13:19"
 * 后者却不支持，原因是 pattern 的 dd 位置只有数字 9，必须要是两位数字才能支持
 *
 *
 * 所以：建议优先使用转换结果为 Date 的 parse 方法，使用 SimpleDateFormat 来转换
 */
@SuppressWarnings("unused")
@RequiresApi(api = Build.VERSION_CODES.O)
public class TimeKit {


    /**
     * 结合 ThreadLocal 缓存 "非线程安全" 的 SimpleDateFormat
     */
    private static final ThreadLocal<HashMap<String, SimpleDateFormat>> TL = ThreadLocal.withInitial(HashMap::new);

    @SuppressLint("SimpleDateFormat")
    public static SimpleDateFormat getSimpleDateFormat(String pattern) {
        SimpleDateFormat ret = Objects.requireNonNull(TL.get()).get(pattern);
        if (ret == null) {
            ret = new SimpleDateFormat(pattern);
            Objects.requireNonNull(TL.get()).put(pattern, ret);
        }
        return ret;
    }
    /**
     * 按指定 pattern 将 Date 转换成 String
     * 例如：format(new Date(), "yyyy-MM-dd HH:mm:ss")
     */
    public static String format(Date date, String pattern) {
        return getSimpleDateFormat(pattern).format(date);
    }

    /**
     * 按指定 pattern 将 String 转换成 Date
     */
    public static Date parse(String dateString, String pattern) {
        try {
            return getSimpleDateFormat(pattern).parse(dateString);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 判断 A 的时间是否在 B 的时间 "之后"
     */
    public static boolean isAfter(ChronoLocalDateTime<?> self, ChronoLocalDateTime<?> other) {
        return self.isAfter(other);
    }

    /**
     * 判断 A 的时间是否在 B 的时间 "之前"
     */
    public static boolean isBefore(ChronoLocalDateTime<?> self, ChronoLocalDateTime<?> other) {
        return self.isBefore(other);
    }

    /**
     * 判断 A 的时间是否与 B 的时间 "相同"
     */
    public static boolean isEqual(ChronoLocalDateTime<?> self, ChronoLocalDateTime<?> other) {
        return self.isEqual(other);
    }

    /**
     * java.util.Date --> java.time.LocalDateTime
     */
    public static LocalDateTime toLocalDateTime(Date date) {
        // java.sql.Date 不支持 toInstant()，需要先转换成 java.util.Date
        if (date instanceof java.sql.Date) {
            date = new Date(date.getTime());
        }

        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        return LocalDateTime.ofInstant(instant, zone);
    }

    /**
     * java.util.Date --> java.time.LocalDate
     */
    public static LocalDate toLocalDate(Date date) {
        // java.sql.Date 不支持 toInstant()，需要先转换成 java.util.Date
        if (date instanceof java.sql.Date) {
            date = new Date(date.getTime());
        }

        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalDate();
    }

    /**
     * java.util.Date --> java.time.LocalTime
     */
    public static LocalTime toLocalTime(Date date) {
        // java.sql.Date 不支持 toInstant()，需要先转换成 java.util.Date
        if (date instanceof java.sql.Date) {
            date = new Date(date.getTime());
        }

        Instant instant = date.toInstant();
        ZoneId zone = ZoneId.systemDefault();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, zone);
        return localDateTime.toLocalTime();
    }

    /**
     * java.time.LocalDateTime --> java.util.Date
     */
    public static Date toDate(LocalDateTime localDateTime) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * java.time.LocalDate --> java.util.Date
     */
    public static Date toDate(LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * java.time.LocalTime --> java.util.Date
     */
    public static Date toDate(LocalTime localTime) {
        LocalDate localDate = LocalDate.now();
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * java.time.LocalTime --> java.util.Date
     */
    public static Date toDate(LocalDate localDate, LocalTime localTime) {
        LocalDateTime localDateTime = LocalDateTime.of(localDate, localTime);
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDateTime.atZone(zone).toInstant();
        return Date.from(instant);
    }
}
